package istar.behaviour;

import istar.IElement;
import istar.ITask;
import istar.agent.IstarJadeAgent;
import istar.impl.AbstractElement;
import istar.impl.MeansEndLink;
import istar.impl.Task;
import istar.impl.TaskDecompositionLink;
import istar.onto.IstarOntology;
import jade.content.lang.Codec;
import jade.content.lang.sl.SLCodec;
import jade.content.onto.Ontology;
import jade.core.AID;
import jade.core.behaviours.Behaviour;
import jade.domain.DFService;
import jade.domain.FIPANames;
import jade.domain.FIPAAgentManagement.DFAgentDescription;
import jade.domain.FIPAAgentManagement.ServiceDescription;
import jade.lang.acl.ACLMessage;
import jade.lang.acl.MessageTemplate;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.Iterator;

import log.Log;

public class RequestExternalElement extends Behaviour implements IistarBehaviour {

	private Codec codec = new SLCodec();
	private Ontology ontology = IstarOntology.getInstance();
	private IElement dependum;
	private IElement dependerElement;
	private int step = 0;
	private boolean log = true;
	private HashMap<String, Integer> qttMsgSent = new HashMap<String, Integer>();
	private HashMap<String, Integer> qttMsgReceived = new HashMap<String, Integer>();
	private String conversationID;
	private ArrayList<Object> contentResponses = new ArrayList<Object>(); 
	
	private boolean error = false;

	
	public RequestExternalElement(IElement _dependum){
		//super();
		step = 0;
		this.dependum = _dependum; 
		this.setBehaviourName("Request_"+ _dependum.getName());
	}

	public RequestExternalElement(IstarJadeAgent _agent, IElement _dependum){
		super(_agent);
		step = 0;
		this.dependum = _dependum; 
		this.setBehaviourName("Request_"+ _dependum.getName());
	}

	public RequestExternalElement(IElement _dependum, ITask _depender){
		super();
		step = 0;
		this.dependum = _dependum; 
		this.setBehaviourName("Request_"+ _dependum.getName());
		this.dependerElement = _depender;
	}
	
	public RequestExternalElement(IstarJadeAgent _agent, IElement _dependum, IElement _dependerElemnt){
		super(_agent);
		step = 0;
		this.dependum = _dependum; 
		this.setBehaviourName("Request_"+ _dependum.getName());
		this.dependerElement = _dependerElemnt;
	}
	
	@Override
	public void action() {
		try {
			myAction();
		} catch (Exception e) {
			error = true;
			this.dependerElement.getMain().getContent().clear();
			this.dependerElement.getMain().getContent().add(e.getMessage());
			//System.out.println(" >>>>> "+e.getMessage());;
		}
	}
	
	@Override
	public int onEnd() {
		if (error){
			((IstarJadeAgent)myAgent).log("Ending " + this.getBehaviourName() + " with error");
			//System.out.println("Ending " + this.getBehaviourName() + " with error");
			return ERROR_STATE_CODE;
		}
		else
			return myOnEnd();
	}
	
	public void myAction() {
		DFAgentDescription[] results = null;

		switch (step) {
		case 0:
			//String execNumber = ((AbstractElement)dependerElement).getExecutionNumber();
			IElement endElement =  dependerElement.getSupports().get(0).getOwner();
			dependerElement.getContent().clear();
			//dependerElement.getContent().addAll(dependerElement.getMain().getContent());
			dependerElement.getContent().addAll(endElement.getContent());
			if (dependerElement.getContent()!=null && dependerElement.getContent().size()>0){
				// 	Search for Dependees
			  	log(myAgent.getName()+" searching for services of type " + dependum.getTopic() +"/>");
			  	try {
			  		// Build the description used as template for the search
			  		DFAgentDescription template = new DFAgentDescription();
			  		ServiceDescription sd = new ServiceDescription();
			  		sd.setType(dependum.getTopic());
	  		  		sd.addOntologies(IstarOntology.getInstance().getName());
	  		  		sd.addLanguages(FIPANames.ContentLanguage.FIPA_SL);
			  		template.addServices(sd);
			  		results = DFService.search(myAgent, template);
			  		if (results.length > 0) {
			  			int qttObjects = dependerElement.getContent().size();
		  				int qttProviders = results.length;
		  				Double floor = Math.floor(qttObjects/qttProviders);
		  				if (floor!= (qttObjects/qttProviders))
		  					floor++;
		  				int objsPerMsg = floor.intValue();
		  				
			  			log(myAgent.getName()+" found the following services providers:");
			  			for (int i = 0; i < results.length; ++i) {
			  				DFAgentDescription dfd = results[i];
	                        AID provider = dfd.getName();
	                        log("Provider name="+ dfd.getName());
	        				ACLMessage msg = new ACLMessage(ACLMessage.REQUEST);
	        				msg.setLanguage(codec.getName());
	        				msg.setOntology(ontology.getName());
	        				msg.addReceiver(provider);
			  				conversationID = dependerElement.getExecutionNumber();
							msg.setConversationId(conversationID);
							ArrayList<String> msgContents = new ArrayList<String>();
	        				if (i!=results.length-1){
	        					for (int j=0;j<objsPerMsg;j++){
	        						int index = i*objsPerMsg + j;
					  				msgContents.add((String)dependerElement.getContent().get(index));
	        					}
			  				}
			  				else {
	        					for (int j=0;j<objsPerMsg;j++){
	        						int index = i*objsPerMsg + j;
	        						if (index<qttObjects)
	        							msgContents.add((String)dependerElement.getContent().get(index));
	        					}
			  				}
	        				
	        				msg.setContentObject(msgContents);
	        				myAgent.send(msg);
	        				if (qttMsgSent.containsKey(conversationID)){
		        				int msgSent = qttMsgSent.remove(conversationID);
					  			msgSent++;
					  			qttMsgSent.put(conversationID, msgSent);
	        				}
	        				else {
					  			qttMsgSent.put(conversationID, new Integer(1));
	        				}
	        				
	        				log("Sending message " + conversationID + " to agent " + provider.getName() );
	        				
						  	for (int k=0;k<msgContents.size();k++){
		        				log("Message content: " + msgContents.get(k));
						  	}
	
			  			}
						step++;
						dependerElement.getContent().clear();
			  		}	
			  		else {
			  			log(myAgent.getName() +" did not find any " + dependum.getTopic()+ " service");
			  		}

			  	}
			  	catch (Exception e) {
			  		e.printStackTrace();
			  	}

			  	

			}
			break;

		case 1:
			//log(" behaviour="+ getBehaviourName() + " status=waiting for response ");
			MessageTemplate mtReply = MessageTemplate.MatchConversationId(conversationID);
			ACLMessage reply = myAgent.receive(mtReply);
			if (reply!=null){
				if (reply.getPerformative()!=ACLMessage.FAILURE){
					Iterator<TaskDecompositionLink> itSuppLinks =  dependerElement.getSupports().iterator();
					while (itSuppLinks.hasNext()){
						IElement superTask = itSuppLinks.next().getOwner();
						superTask.getContent().clear();
					}

					if (qttMsgReceived.containsKey(conversationID)){
						Integer qttRec = qttMsgReceived.remove(conversationID);
						qttRec++;
						qttMsgReceived.put(conversationID, qttRec);
					}
					else {
						qttMsgReceived.put(conversationID, new Integer(1));
					}

					try {
						//ArrayList<String> replyContent = (ArrayList<String>)reply.getContentObject();
						//contentResponses.addAll(replyContent);
						contentResponses.add(reply.getContentObject());
						log("Received a REPLY about " + conversationID + " from " + reply.getSender().getName() + " with content:" + reply.getContentObject().toString());
						
					} catch (Exception e) {
						e.printStackTrace();
					}
					if (qttMsgReceived.get(conversationID).equals(qttMsgSent.get(conversationID))){
						
						dependerElement.getContent().clear();
						dependerElement.getContent().addAll(contentResponses);
						((IstarJadeAgent)myAgent).setKnowledgeElement(dependerElement.getExecutionNumber(), dependerElement);
						
						Iterator<TaskDecompositionLink> itSupps =  dependerElement.getSupports().iterator();
						while (itSupps.hasNext()){
							IElement superTask = itSupps.next().getOwner();
							//superTask.getContent().clear();
							superTask.getContent().addAll(contentResponses);
							((IstarJadeAgent)myAgent).setKnowledgeElement(dependerElement.getExecutionNumber(), superTask);
						}
						
						if (dependerElement.getClass().equals(Task.class)){
							Iterator<MeansEndLink> itMeansFor =  ((Task)dependerElement).getMeansFor().iterator();
							while (itMeansFor.hasNext()){
								IElement end = itMeansFor.next().getOwner();
								end.getContent().clear();
								end.getContent().addAll(contentResponses);
								((IstarJadeAgent)myAgent).setKnowledgeElement(dependerElement.getExecutionNumber(), end);
								
							}
							
						}

						step++;
					}
				}
				else {
					log(" *** Received a REPLY about " + conversationID + " from " + reply.getSender().getName() + " with FAILURE status. *** ");
					step--;
				}
					

			}
			
			break;

		case 2:
			//log("behaviour type=" + getClass().getName() + " name="+ getBehaviourName() + " ending ");
			step++;
			break;
		
		default:
			break;
		}
		
	}

	
	@Override
	public boolean done() {
		if (step==3){
			step = 0;
			log("behaviour name="+ getBehaviourName() + " status=done ");
			return true;
		}
		return false;
	}
	
	
	public int myOnEnd() {
		//log("<behaviour type=" + getClass().getName() + " name="+ getBehaviourName() + " ending />");
		if (dependerElement!=null){
			if (dependerElement.achieved())
				return 1;
			else
				return 1;
		}
		else
			return 2;
	}
	
	
	public void log(String _phrase){
		if (this.log){
			((IstarJadeAgent)myAgent).log( _phrase);
		}
	}

	@Override
	public String getExecutionNumber() {
		return this.dependerElement.getExecutionNumber();
	}

	
	
}
